<template>
    <div class="tour-popover">
        <div
            :style="[popoverBoxStyle, { opacity: visible ? 1 : 0}]"
            :class="{ 'fade-in': !!visible, 'fade-out': !visible }"
            ref="popoverBoxRef"
            class="tour-content"
        >
            <div :class="`triangle-${triangle}`" class="tour-middle">
                <slot name="header"></slot>

                <div class="tour-inner">
                    <slot name="content"></slot>
                </div>
                
                <slot name="footer"></slot>
            </div>
        </div>

        <div class="view">
            <slot></slot>
        </div>
    </div>
</template>

<script setup>
import { ref, reactive, onMounted, watch, nextTick, computed } from 'vue'
import {
    normalTopMap,
    autoReverseTopMap,
    normalBottomMap,
    autoReverseBottomMap,
    normalLeftMap,
    autoReverseLeftMap,
    normalRightMap,
    autoReverseRightMap,
    allDirectionMap,
    priority
} from './tools'
// 样式
import '../../style/popover.css'

const visible = defineModel()
const props = defineProps({
    // 视口dom信息
    viewStyle: {
        type: Object,
        default: () => ({})
    },
    // 偏移量
    offset: {
        type: Number,
        default: 10
    },
    // 出现在视口的什么位置
    placement: {
        type: String,
        default: 'top'
    }
})

const popoverBoxRef = ref() // popoverBox dom
const popoverBoxInfo = ref({}) // popoverBox dom 信息
const popoverBoxStyle = reactive({}) // 具体样式
const triangle = ref('top') // 三角形的方向 默认向下

// const placement = computed(() => {
//     // 如果传入的placement不在所有方向中，则默认为top
//     if (!allDirectionMap.includes(props.placement)) {
//         return 'top'
//     }
//     return props.placement
// })

const placement = ref('top')

watch(() => props.placement, (val) =>  {
    if (!allDirectionMap.includes(props.placement)) {
        placement.value = 'top'
    } else {
        placement.value = val
    }
}, { deep: true, immediate: true })

const PLACEMENT = {
    // popover是否在屏幕内 true表示在屏幕内，false表示在屏幕外
    inScreen: function({ top, left, width, height }) {
        const condition = 
            parseFloat(top) - props.offset - popoverBoxInfo.value.height >= 0 &&
            parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height <= window.innerHeight &&
            parseFloat(left) - props.offset - popoverBoxInfo.value.width >= 0 &&
            parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width <= window.innerWidth
        return condition
    },
    // 顶部优先判断处理函数
    topPriorityHandler: function() {
        const { top, left, width, height } = props.viewStyle
        // if (parseFloat(top) - props.offset - popoverBoxInfo.value.height < 0) {
        //     triangle.value = autoReverseTopMap[placement.value]
        //     popoverBoxStyle.top = `calc(${top} + ${height} + ${props.offset}px)`
        // } else {
        //     triangle.value = normalTopMap[placement.value]
        //     popoverBoxStyle.top = `calc(${top} - ${props.offset}px - ${popoverBoxInfo.value.height}px)`
        // }

        /* 
            反转之前，先要判断反转过去会不会超出屏幕，如果会超出屏幕，则判断其他方向是否也都超出屏幕，如果都超出了，则不反转, 否则展示到对应的位置， true表示会超出屏幕
            判断优先级: 当前位置 > 反转位置 > 上/下 > 右/左
        */
        if (parseFloat(top) - props.offset - popoverBoxInfo.value.height >= 0) {
            // placement.value = props.placement
            triangle.value = normalTopMap[placement.value]
            popoverBoxStyle.top = `calc(${top} - ${props.offset}px - ${popoverBoxInfo.value.height}px)`
        } else {
            if (parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height <= window.innerHeight && (popoverBoxInfo.value.left + popoverBoxInfo.value.width <= window.innerWidth)) {
                triangle.value = autoReverseTopMap[placement.value]
                popoverBoxStyle.top = `calc(${top} + ${height} + ${props.offset}px)`
            } else if (parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width <= window.innerWidth) {
                placement.value = priority[placement.value].right
                this.rightPriorityHandler(placement.value)
                return
            } else if (parseFloat(left) - props.offset - popoverBoxInfo.value.width >= 0) {
                popoverBoxStyle.left = 0
                popoverBoxStyle.top = 0
                nextTick(() => {
                    popoverBoxInfo.value = popoverBoxRef.value?.getBoundingClientRect()
                    placement.value = priority[placement.value].left
                    this.leftPriorityHandler(placement.value)
                })
              
                return
            } else {
                placement.value = props.placement
                triangle.value = normalTopMap[placement.value]
                popoverBoxStyle.top = `calc(${top} - ${props.offset}px - ${popoverBoxInfo.value.height}px)`
            }
        }

        // 设置popover左右位置
        if (placement.value === 'top' || placement.value === 'bottom') {
            popoverBoxStyle.left = `calc(${left} + ${parseFloat(width) / 2}px - ${parseFloat(popoverBoxInfo.value.width) / 2}px)`
        }

        if (placement.value === 'topLeft' || placement.value === 'bottomLeft') {
            popoverBoxStyle.left = left
        }

        if (placement.value === 'topRight' || placement.value === 'bottomRight') {
            popoverBoxStyle.left = `calc(${left} + ${width} - ${parseFloat(popoverBoxInfo.value.width)}px)`
        }
    },
    // 底部优先判断处理函数
    bottomPriorityHandler: function() {
        const { top, left, width, height } = props.viewStyle
        // 如果 parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height > window.innerHeight 则向上显示
        // if (parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height > window.innerHeight) {
        //     triangle.value = autoReverseBottomMap[placement.value]
        //     popoverBoxStyle.top = `calc(${top} - ${props.offset}px - ${popoverBoxInfo.value.height}px)`
        // } else {
        //     triangle.value = normalBottomMap[placement.value]
        //     popoverBoxStyle.top = `calc(${top} + ${height} + ${props.offset}px)`
        // }

        if (parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height <= window.innerHeight) {
            // placement.value = props.placement
            triangle.value = normalBottomMap[placement.value]
            popoverBoxStyle.top = `calc(${top} + ${height} + ${props.offset}px)`
        } else {
            if (parseFloat(top) - props.offset - popoverBoxInfo.value.height >= 0 && (popoverBoxInfo.value.left + popoverBoxInfo.value.width <= window.innerWidth)) {
                triangle.value = autoReverseBottomMap[placement.value]
                popoverBoxStyle.top = `calc(${top} - ${props.offset}px - ${popoverBoxInfo.value.height}px)`
            } else if (parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width <= window.innerWidth) {
                placement.value = priority[placement.value].right
                this.rightPriorityHandler(placement.value)
                return
            } else if (parseFloat(left) - props.offset - popoverBoxInfo.value.width >= 0) {
                popoverBoxStyle.left = 0
                popoverBoxStyle.top = 0
                nextTick(() => {
                    popoverBoxInfo.value = popoverBoxRef.value?.getBoundingClientRect()
                    placement.value = priority[placement.value].left
                    this.leftPriorityHandler(placement.value)
                })
                return
            } else {
                placement.value = props.placement
                triangle.value = normalBottomMap[placement.value]
                popoverBoxStyle.top = `calc(${top} + ${height} + ${props.offset}px)`
            }
        }

        // 设置popover左右位置
        if (placement.value === 'top' || placement.value === 'bottom') {
            popoverBoxStyle.left = `calc(${left} + ${parseFloat(width) / 2}px - ${parseFloat(popoverBoxInfo.value.width) / 2}px)`
        }

        if (placement.value === 'topLeft' || placement.value === 'bottomLeft') {
            popoverBoxStyle.left = left
        }

        if (placement.value === 'topRight' || placement.value === 'bottomRight') {
            popoverBoxStyle.left = `calc(${left} + ${width} - ${parseFloat(popoverBoxInfo.value.width)}px)`
        }
    },
    // 左部优先判断处理函数
    leftPriorityHandler: function() {
        const { top, left, width, height } = props.viewStyle
        // if (parseFloat(left) - props.offset - popoverBoxInfo.value.width < 0) {
        //     triangle.value = autoReverseLeftMap[placement.value]
        //     popoverBoxStyle.left = `calc(${left} + ${width} + ${props.offset}px)`
        // } else {
        //     triangle.value = normalLeftMap[placement.value]
        //     popoverBoxStyle.left = `calc(${left} - ${props.offset}px - ${popoverBoxInfo.value.width}px)`
        // }

        if (parseFloat(left) - props.offset - popoverBoxInfo.value.width >= 0) {
            // placement.value = props.placement
            triangle.value = normalLeftMap[placement.value]
            popoverBoxStyle.left = `calc(${left} - ${props.offset}px - ${popoverBoxInfo.value.width}px)`
        } else {
            if (parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width <= window.innerWidth && (popoverBoxInfo.value.top + popoverBoxInfo.value.height <= window.innerHeight)) {
                triangle.value = autoReverseLeftMap[placement.value]
                popoverBoxStyle.left = `calc(${left} + ${width} + ${props.offset}px)`
            } else if (parseFloat(top) - props.offset - popoverBoxInfo.value.height >= 0) {
                popoverBoxStyle.left = 0
                popoverBoxStyle.top = 0
                nextTick(() => {
                    popoverBoxInfo.value = popoverBoxRef.value?.getBoundingClientRect()
                    placement.value = priority[placement.value].top
                    this.topPriorityHandler(placement.value)
                })
                return
            } else if (parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height <= window.innerHeight) {
                placement.value = priority[placement.value].bottom
                this.bottomPriorityHandler(placement.value)
                return
            } else {
                placement.value = props.placement
                triangle.value = normalLeftMap[placement.value]
                popoverBoxStyle.left = `calc(${left} - ${props.offset}px - ${popoverBoxInfo.value.width}px)`
            }
        }

        // 设置popover上下位置
        if (placement.value === 'left' || placement.value === 'right') {
            popoverBoxStyle.top = `calc(${top} + ${parseFloat(height) / 2}px - ${parseFloat(popoverBoxInfo.value.height) / 2}px)`
        }

        if (placement.value === 'leftTop' || placement.value === 'rightTop') {
            popoverBoxStyle.top = top
        }

        if (placement.value === 'leftBottom' || placement.value === 'rightBottom') {
            popoverBoxStyle.top = `calc(${top} + ${height} - ${parseFloat(popoverBoxInfo.value.height)}px)`
        }
    },
    // 右部优先判断处理函数
    rightPriorityHandler: function() {
        const { top, left, width, height } = props.viewStyle
        /* 
            反转之前，先要判断反转过去会不会超出屏幕，如果会超出屏幕，则判断其他方向是否也都超出屏幕，如果都超出了，则不反转, 否则展示到对应的位置， true表示所有方向都会超出屏幕
            判断优先级: 当前位置 > 反转位置 > 上/下 > 右/左
        */
        // if (parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width > window.innerWidth) {
        //     triangle.value = autoReverseRightMap[placement.value]
        //     popoverBoxStyle.left = `calc(${left} - ${props.offset}px - ${popoverBoxInfo.value.width}px)`
        // } else {
        //     triangle.value = normalRightMap[placement.value]
        //     popoverBoxStyle.left = `calc(${left  } + ${width} + ${props.offset}px)`
        // }

        if (parseFloat(left) + parseFloat(width) + props.offset + popoverBoxInfo.value.width <= window.innerWidth) {
            // placement.value = props.placement
            triangle.value = normalRightMap[placement.value]
            popoverBoxStyle.left = `calc(${left} + ${width} + ${props.offset}px)`
        } else {
            if (parseFloat(left) - props.offset - popoverBoxInfo.value.width >= 0 && (popoverBoxInfo.value.top + popoverBoxInfo.value.height <= window.innerHeight)) {
                triangle.value = autoReverseRightMap[placement.value]
                popoverBoxStyle.left = `calc(${left} - ${props.offset}px - ${popoverBoxInfo.value.width}px)`
            } else if (parseFloat(top) - props.offset - popoverBoxInfo.value.height >= 0) {
                popoverBoxStyle.left = 0
                popoverBoxStyle.top = 0
                nextTick(() => {
                    popoverBoxInfo.value = popoverBoxRef.value?.getBoundingClientRect()
                    placement.value = priority[placement.value].top
                    this.topPriorityHandler(placement.value)
                })
                return
            } else if (parseFloat(top) + parseFloat(height) + props.offset + popoverBoxInfo.value.height <= window.innerHeight) {
                placement.value = priority[placement.value].bottom
                this.bottomPriorityHandler(placement.value)
                return
            } else {
                placement.value = props.placement
                triangle.value = normalRightMap[placement.value]
                popoverBoxStyle.left = `calc(${left} + ${width} + ${props.offset}px)`
            }
        }

        // 设置popover上下位置
        if (placement.value === 'left' || placement.value === 'right') {
            popoverBoxStyle.top = `calc(${top} + ${parseFloat(height) / 2}px - ${parseFloat(popoverBoxInfo.value.height) / 2}px)`
        }

        if (placement.value === 'leftTop' || placement.value === 'rightTop') {
            popoverBoxStyle.top = top
        }

        if (placement.value === 'leftBottom' || placement.value === 'rightBottom') {
            popoverBoxStyle.top = `calc(${top} + ${height} - ${parseFloat(popoverBoxInfo.value.height)}px)`
        }
    },
    // 出现在顶部
    top: function() {
        this.topPriorityHandler('top')
    },
    topLeft: function() {
        this.topPriorityHandler('topLeft')
    },
    topRight: function() {
        this.topPriorityHandler('topRight')
    },
    // 出现在底部
    bottom: function() {
        this.bottomPriorityHandler('bottom')
    },
    bottomLeft: function() {
        this.bottomPriorityHandler('bottomLeft')
    },
    bottomRight: function() {
        this.bottomPriorityHandler('bottomRight')
    },
    // 出现在左部
    left: function() {
        this.leftPriorityHandler('left')
    },
    leftTop: function() {
        this.leftPriorityHandler('leftTop')
    },
    leftBottom: function() {
        this.leftPriorityHandler('leftBottom')
    },
    // 出现在右部
    right: function() {
        this.rightPriorityHandler('right')
    },
    rightTop: function() {
        this.rightPriorityHandler('rightTop')
    },
    rightBottom: function() {
        this.rightPriorityHandler('rightBottom')
    }
}

// 更新位置
/*
 位置改变规则
 1、如果 top 位置放不下 则 设置为对应的bottom
 2、如果 topLeft 位置放不下 则 设置为对应的bottomLeft
 3、如果 topRight 位置放不下 则 设置为对应的bottomRight
 4、如果 bottom 位置放不下 则 设置为对应的top
 5、如果 bottomLeft 位置放不下 则 设置为对应的topLeft
 6、如果 bottomRight 位置放不下 则 设置为对应的topRight
 7、如果 left 位置放不下 则 设置为对应的right
 8、如果 leftTop 位置放不下 则 设置为对应的rightTop
 9、如果 leftBottom 位置放不下 则 设置为对应的rightBottom
 10、如果 right 位置放不下 则 设置为对应的left
 11、如果 rightTop 位置放不下 则 设置为对应的leftTop
 12、如果 rightBottom 位置放不下 则 设置为对应的leftBottom
*/

const updatePosition = () => {
    placement.value = props.placement
    popoverBoxInfo.value = popoverBoxRef.value?.getBoundingClientRect()
    nextTick(() => {
        PLACEMENT[placement.value]()
    })
}

// 漫游视口变了，或者朝向变了，更新位置
watch(() => [props.viewStyle, props.placement], updatePosition, { deep: true })
watch(() => visible.value, (newV) => {
    if (newV) {
        updatePosition()
    } else {
        // 这个目的是为了获取popoverBoxRef.value的真实宽高, 避免被屏幕挤压
        popoverBoxStyle.left = 0
        popoverBoxStyle.top = 0
    }
})
</script>